#include "OpenCLWrapper.hpp"
#include <jni.h>
#include <string>
#include <sstream>
#include <iostream>
#include <android/log.h>
#include <dlfcn.h>
#include <malloc.h>
#include <android/native_window_jni.h>

int initOpenCLLibs() {
    OpenCLSymbolsOperator::createOpenCLSymbolsOperatorSingleInstance();
    if (nullptr == OpenCLSymbolsOperator::getOpenclSymbolsPtr()) {
        LOGI("OpenCL init error, fallback ... \n");
        return -1;
    }
    if (true == OpenCLSymbolsOperator::getOpenclSymbolsPtr()->isError()) {
        LOGI("Parsing OpenCL symbols error !!! \n");
        return -1;
    }
    return 0;
}

std::string CheckYourPlatform() {
    std::stringstream ss;
    ss << "testcase001, check your platform!"<<std::endl;
    ss <<"---- begin ---"<<std::endl;;

    int ret = initOpenCLLibs();
    if(ret < 0) {
        ss <<"---- end ---"<<std::endl;;
        std::string result(ss.str());
        return result;
    }
    // get platform
    std::vector<cl::Platform> platforms;
    cl::Platform::get(&platforms);
    if (platforms.empty()) {
        ss << "No OpenCL platforms found." << std::endl;
        ss <<"---- end ---"<<std::endl;;
        std::string result(ss.str());
        return result;
    }

    // get 1st platform
    cl::Platform platform = platforms[0];
    // get gpu devices
    std::vector<cl::Device> devices;
    platform.getDevices(CL_DEVICE_TYPE_GPU, &devices);
    if (devices.empty()) {
        ss << "No OpenCL devices found." << std::endl;
        ss <<"---- end ---"<<std::endl;;
        std::string result(ss.str());
        return result;
    }

    cl::Device device = devices[0];
    // print the device name
    std::string value;
    device.getInfo(CL_DEVICE_NAME, &value);
    ss << "Device Name: "<< value<< std::endl;
    device.getInfo(CL_DRIVER_VERSION, &value);
    ss << "Device driver version: "<< value<< std::endl;
    device.getInfo(CL_DEVICE_VERSION, &value);
    ss << "Device device version: "<< value<< std::endl;
    device.getInfo(CL_DEVICE_OPENCL_C_VERSION, &value);
    ss << "Device device C version: "<< value<< std::endl;

    // print the device compute info
    cl_uint maxComputeUnits=0;
    device.getInfo(CL_DEVICE_MAX_COMPUTE_UNITS, &maxComputeUnits);
    ss << "Parallel compute units: "<< maxComputeUnits<< std::endl;

    cl_uint maxClockFreq=0;
    device.getInfo(CL_DEVICE_MAX_CLOCK_FREQUENCY, &maxClockFreq);
    ss << "maxClockFreq: "<< maxClockFreq<< std::endl;

    size_t maxParaSize=0;
    device.getInfo(CL_DEVICE_MAX_PARAMETER_SIZE, &maxParaSize);
    ss << "maxParaSize: "<< maxParaSize<< std::endl;

    cl_device_fp_config fpconf;
    device.getInfo(CL_DEVICE_SINGLE_FP_CONFIG, &fpconf);
    LOGI("fp config = denorm(%u), roundToNearest(%u), "
           "roundToZero(%u), roundToInf(%u), fma(%u), softfp(%u), roundDivSqrt(%u)\n",
           bool(fpconf&CL_FP_DENORM), bool(fpconf&CL_FP_ROUND_TO_NEAREST),
           bool(fpconf&CL_FP_ROUND_TO_ZERO), bool(fpconf&CL_FP_ROUND_TO_INF),
           bool(fpconf&CL_FP_FMA), bool(fpconf&CL_FP_SOFT_FLOAT),
           bool(fpconf&CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)
    );

    cl_bool supportUnifiedMemory;
    device.getInfo(CL_DEVICE_HOST_UNIFIED_MEMORY, &supportUnifiedMemory);
    ss << "supportUnifiedMemory: "<< supportUnifiedMemory<< std::endl;

    device.getInfo(CL_DEVICE_BUILT_IN_KERNELS, &value);
    ss << "device built-in kernelList: "<< value<< std::endl;


    cl_uint maxWorkItemDim;
    device.getInfo(CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, &maxWorkItemDim);
    ss << "maxWorkItemDimension: "<< maxWorkItemDim<< std::endl;

    size_t maxWorkItemSize[10];
    device.getInfo(CL_DEVICE_MAX_WORK_ITEM_SIZES, &(maxWorkItemSize));
    ss << "maxWorkItemSize: ";
    for (uint64_t i=0; i < maxWorkItemDim; i++) {
        ss << maxWorkItemSize[i];
        if (i==maxWorkItemDim-1) {
            ss << std::endl;
        } else {
            ss << "x";
        }
    }

    cl_ulong maxGlobalMemSize=0;
    device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &maxGlobalMemSize);
    ss << "maxGlobalMemSize(MB): "<< maxGlobalMemSize/1024/1024<< std::endl;

    cl_ulong maxConstantBufferSize=0;
    device.getInfo(CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, &maxConstantBufferSize);
    ss << "maxConstantBufferSize(KB): "<< maxConstantBufferSize/1024<< std::endl;

    cl_ulong maxLocalMemSize=0;
    device.getInfo(CL_DEVICE_LOCAL_MEM_SIZE, &maxLocalMemSize);
    ss << "maxLocalMemSize(KB): "<< maxLocalMemSize/1024<< std::endl;
    ss <<"---- end ---"<<std::endl;;
    std::string result(ss.str());
    return result;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myopencldemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "hi";
    std::string loginfo = CheckYourPlatform();

    return env->NewStringUTF(loginfo.c_str());
}